package com.cs.framework.jdbc.common.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;

import com.cs.framework.common.util.ReflectUtils;
import com.cs.framework.common.util.StringUtils;
import com.cs.framework.jdbc.common.bo.PagingInfo;

/**
 * SQL信息
 * 
 * @author hedongzhou
 * 
 */
public class DBSqlUtils {

	/**
	 * SQL主体语句匹配符
	 */
	public static final String SQL_PATTERN = "#sql#";

	/**
	 * 开始索引匹配符
	 */
	public static final String START_INDEX_PATTERN = "#startIndex#";

	/**
	 * 每页最大行数匹配符
	 */
	public static final String PAGE_SIZE_PATTERN = "#pageSize#";

	/**
	 * 合计SQL
	 */
	public static final String COUNT_SQL = "SELECT COUNT(*) FROM (#sql#) tb";

	/**
	 * 分页SQL
	 * 
	 * @author hedongzhou
	 * 
	 */
	public static enum PagingSql {
		mysql("#sql# limit #startIndex#, #pageSize#"), // MySql
		oracle(
				"SELECT * FROM (SELECT rownum r, tb.* FROM (#sql#) tb) WHERE r > #startIndex# AND r <= #startIndex# + #pageSize#"); // Oracle

		/**
		 * 分页SQL
		 */
		private String pagingSql;

		PagingSql(String pagingSql) {
			this.pagingSql = pagingSql;
		}

		public String getPagingSql() {
			return pagingSql;
		}
	}

	/**
	 * 获取合计SQL
	 * 
	 * @param sql
	 * @return
	 */
	public static String getCountSql(String sql) {
		return StringUtils.replace(COUNT_SQL, SQL_PATTERN, sql);
	}

	/**
	 * 获取分页SQL
	 * 
	 * @param sql
	 *            原SQL
	 * @param pagingInfo
	 *            分页信息
	 * @param databaseProductName
	 *            数据库产品名称
	 * @return
	 */
	public static String getPagingSql(String sql, PagingInfo pagingInfo, String databaseProductName) {
		databaseProductName = databaseProductName.toLowerCase();

		for (PagingSql pagingSql : PagingSql.values()) {
			if (pagingSql.name().equals(databaseProductName)) {
				sql = StringUtils.replace(pagingSql.getPagingSql(), SQL_PATTERN, sql);
				sql = StringUtils.replace(sql, START_INDEX_PATTERN, pagingInfo.getStartIndex().toString());
				sql = StringUtils.replace(sql, PAGE_SIZE_PATTERN, pagingInfo.getPageSize().toString());
				break;
			}
		}

		return sql;
	}

	/**
	 * 获取SQL参数
	 * 
	 * @param boundSql
	 *            SQL绑定对象
	 * @param parameter
	 *            参数
	 * @return
	 */
	public static List<Object> getSqlParameter(BoundSql boundSql) {
		Object parameter = boundSql.getParameterObject();
		List<Object> params = new ArrayList<Object>();

		if (ReflectUtils.isBaseType(parameter)) {
			params.add(parameter);
		} else {
			List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
			if (parameterMappings != null) {
				MetaObject metaObject = MetaObject.forObject(parameter, new DefaultObjectFactory(),
						new DefaultObjectWrapperFactory());

				for (ParameterMapping parameterMapping : parameterMappings) {
					if (parameterMapping.getMode() != ParameterMode.OUT) {
						Object value = null;

						String propertyName = parameterMapping.getProperty();
						if (boundSql.hasAdditionalParameter(propertyName)) {
							value = boundSql.getAdditionalParameter(propertyName);
						} else if (parameter == null) {
							value = null;
						} else {
							value = metaObject == null ? null : metaObject.getValue(propertyName);
						}

						params.add(value);
					}
				}
			}
		}

		return params;
	}

	/**
	 * 转换参数
	 * 
	 * @param object
	 * @return
	 */
	public static Object transformParameter(Object object) {
		if (object != null) {
			if (object instanceof List) {
				StrictMap<Object> map = new StrictMap<Object>();
				map.put("list", object);
				return map;
			} else if (object != null && object.getClass().isArray()) {
				StrictMap<Object> map = new StrictMap<Object>();
				map.put("array", object);
				return map;
			} else if (!(object instanceof Map) && !ReflectUtils.isBaseType(object)) {
				return ReflectUtils.getFieldValue(object);
			}
		}

		return object;
	}

}
